--[[
-- a unit is declared as a table, having the following entries :

unit = {
	name = string,
	is_mob = boolean,
	is_dual_wielding = boolean,
	can_parry = boolean,
	can_block = boolean,
	
	level = number,
	class = string,
	race = string,

	agility = number,
	
	defense_skill = number,
	weapon_skill = table,
	
	talent_weapon_bonus = table, -- skill
	talent_defense_bonus = number,
	talent_hit_bonus = number,
	talent_dodge_bonus = number,
	talent_parry_bonus = number,

	hit_rating = number,
	dodge_rating = number,
	weapon_rating = table,
	defense_rating = number,
	parry_rating = number,
	haste_rating = number,
}

]]

-- some static data
local combat_table_order = {
	"MISS", 
	"DODGE", 
	"PARRY", 
	"BLOCK", 
	"GLANCING_BLOW", 
	"HIT", 
	"CRUSHING_BLOW", 
	"CRITICAL_HIT"
}

local combat_table_dps_multiplier = {
	MISS = 0,
	DODGE = 0,
	PARRY = 0,
	BLOCK = "BLOCK",
	GLANCING_BLOW = "GLANCING",
	HIT = 1,
	CRUSHING_BLOW = 1.5,
	CRITICAL_HIT = "CRIT",
}

local combat_rating_map = {
	CR_WEAPON = 2.5,
	CR_WEAPON_DAGGER = 2.5,
	CR_WEAPON_SWORD = 2.5,
	CR_WEAPON_SWORD_2H = 2.5,
	CR_WEAPON_AXE = 2.5,
	CR_WEAPON_AXE_2H = 2.5,
	CR_WEAPON_MACE = 2.5,
	CR_WEAPON_MACE_2H = 2.5,
	CR_WEAPON_BOW = 2.5,
	CR_WEAPON_CROSSBOW = 2.5,
	CR_WEAPON_GUN = 2.5,
	CR_WEAPON_FIST = 2.5,
	CR_WEAPON_STAFF = 2.5,
	CR_WEAPON_POLEARM = 2.5,

	CR_DEFENSE = 1.5,
	CR_DODGE = 12,
	CR_PARRY = 15,
	CR_BLOCK = 5,
	CR_HIT = 10,
	CR_CRIT = 14,
	CR_RANGEDHIT = 10,
	CR_RANGEDCRIT = 14,
	CR_HASTE = 10,
	CR_RANGEDHASTE = 10,
	CR_SPELLHIT = 8,
	CR_SPELLCRIT = 14,
	CR_SPELLHASTE = 10,
	CR_RESILIENCE = 25,
	-- does this exist in game ?
	CR_HIT_TAKEN = 10,
	CR_RANGEDHIT_TAKEN = 10,
	CR_SPELLHIT_TAKEN = 8,
	CR_EXPERTISE = 2.5,
}

local base_info = {
	-- reverse engineered via playerdb_info
	dodge = {
		base = "agility",
		WARRIOR = { 0.37, 1.58, 0.72, -17.84, 0.91, }, -- 1.4, 1.7, 32.4, 132.5
		PALADIN = { 0.30, 1.12, 0.47, -8.72, 0.80, }, -- 1.0, 0.6, 24.3, 102.2
		SHAMAN = { 0.25, 4.13, 0.55, -13.62, 1.68, }, -- 0.3, 0.7, 25.1, 83.4
		ROGUE = { 0.25, -1.00, 0.51, -15.77, -0.53, }, -- 0.2, 0.9, 19.8, 109.3
		MAGE = { 0.17, 11.88, 0.21, 10.08, 3.45, }, -- 0.3, 0.7, 25.0, 38.6
		WARLOCK = { 0.27, 4.16, 0.45, -6.58, 2.06, }, -- 0.7, 0.3, 24.7, 72.5
		PRIEST = { 0.19, 9.96, 0.28, 5.36, 3.17, }, -- 0.3, 1.0, 24.9, 45.4
		HUNTER = { 0.34, -0.55, 0.43, -5.02, -5.44, }, -- 0.3, 0.7, 25.0, 260.8
		DRUID = { 0.16, 1.82, 0.25, -2.95, -1.91, }, -- 0.2, 0.5, 14.4, 99.5
	},
	crit = {
		base = "agility",
		WARRIOR = { 0.29, 1.77, 1.30, -58.15, 1.14, }, -- 0.2, 0.7, 33.0, 127.5
		PALADIN = { 0.27, 2.46, 0.54, -12.69, 0.66, }, -- 0.2, 0.8, 25.1, 108.9
		SHAMAN = { 0.24, 4.21, 0.55, -13.29, 1.67, }, -- 0.2, 0.7, 25.1, 83.4
		ROGUE = { 0.49, -2.04, 1.17, -41.64, -0.29, }, -- 0.2, 1.2, 40.5, 214.3
		MAGE = { 0.17, 11.98, 0.21, 10.08, 3.46, }, -- 0.3, 0.7, 25.0, 38.5
		WARLOCK = { 0.24, 4.80, 0.46, -7.57, 2.00, }, -- 0.2, 0.7, 24.6, 73.8
		PRIEST = { 0.19, 9.76, 0.28, 5.36, 3.18, }, -- 0.2, 0.7, 24.9, 45.2
		HUNTER = { 0.56, -1.28, 0.69, -8.46, -1.53, }, -- 0.3, 0.8, 40.0, 261.4
		DRUID = { 0.27, 3.06, 0.47, -7.79, 0.96, }, -- 0.2, 0.9, 25.1, 101.5
	},
	spellcrit = {
		base = "intellect",
		WARRIOR = { 0, 0, 0, 0, -1.00, },
		PALADIN = { 0.76, 6.18, 2.20, -72.21, 3.33, }, -- 0.8, 7.6, 81.5, 135.7
		SHAMAN = { 0.93, -0.30, 2.23, -75.21, 2.20, }, -- 0.5, 3.0, 80.9, 226.6
		ROGUE = { 0, 0, 0, 0, -1.00, },
		MAGE = { 1.20, -4.43, 1.03, 5.78, 0.91, }, -- 0.8, 0.1, 78.1, 319.6
		WARLOCK = { 1.02, -2.09, 2.17, -69.16, 1.70, }, -- 0.6, 1.6, 82.6, 272.4
		PRIEST = { 1.15, -4.03, 1.38, -16.78, 1.24, }, -- 0.5, 1.0, 79.9, 300.2
		HUNTER = { 0.87, 7.32, 1.92, -53.80, 3.60, }, -- 0.7, 2.1, 80.6, 112.4
		DRUID = { 1.01, -1.23, 2.00, -59.81, 1.85, }, -- 0.5, 1.1, 80.4, 253.1
	}
}

local function GetBaseDataPoint(type, unit)
	local info = base_info[type]
	local numbers = info[unit.class]
	local level = unit.level
	local a, b
	if level < 60 then
		a, b = numbers[1], numbers[2]
	else
		a, b = numbers[3], numbers[4]
	end
	local m = a * level + b
	return (m == 0) and 0 or (unit[info.base] / m + numbers[5])
end

-- return the ratio of percent / rating point, dependant on the unit level
local function GetRatingMultiplier(level)
	if level < 9 then
		return 52
	elseif level <= 60 then
		return 52 / (level - 8)
	elseif level <= 70 then
		return 3.1951219512195124 - 0.036585365853658534 * level
	end
end

-- return the percent value of a rating, dependant on the type, rating value and unit level.
local function GetRatingBonus(type, value, level)
	local F = combat_rating_map[type]
	if not F then
		return nil
	end
	if not value then
		return 0
	end
	return value / F * GetRatingMultiplier(level)
end

--~ print(GetRatingBonus("CR_DODGE", 182, 70))

-- return the total weapon skill bonus of the unit for the given weapon type.
local function GetWeaponSkillBonus(unit, wtype)
	if unit.is_mob then
		return unit.level * 5
	end
	local value = unit.weapon_skill[wtype] or 0
	
	value = value + (unit.talent_weapon_bonus and unit.talent_weapon_bonus[wtype] or 0)
	value = value + GetRatingBonus("CR_WEAPON_"..wtype:upper(), unit.weapon_rating[wtype], unit.level)

	return value
end

local function GetExpertiseBonus(unit)
	if unit.is_mob then
		return 0
	end
	local value = unit.talent_expertise_bonus or 0
	value = value + GetRatingBonus("CR_EXPERTISE", unit.expertise_rating, unit.level)
	
	return value
end

-- return the total defense bonus of the unit.
local function GetDefenseBonus(unit)
	if unit.is_mob then
		return unit.level * 5
	end
	local value = unit.defense_skill or 0

	value = value + (unit.talent_defense_bonus or 0)
	value = value + GetRatingBonus("CR_DEFENSE", unit.defense_rating, unit.level)

	return value
end

-- return the difference between the weapon skill of the attacker and the defense of the target.
local function GetRelativeDefenseValue(attacker, target, base_defense_only, base_skill_only)
	local weapon_skill, defense
	if not attacker.is_mob then
		weapon_skill = GetWeaponSkillBonus(attacker, attacker.right_hand_weapon.type)
		if base_skill_only then
			weapon_skill = math.min(weapon_skill, attacker.level * 5)
		end
	else
		weapon_skill = attacker.level * 5
	end
	if not target.is_mob then
		defense = GetDefenseBonus(target)
		if base_defense_only then
			defense = math.min(defense, target.level * 5)
		end
	else
		defense = target.level * 5
	end
	return defense - weapon_skill
end

-- return the base miss rate of the attacker
local function GetBaseAttackerMissChance(attacker)
	if attacker.is_mob then
		return 5
	end
	local value = 5
	if attacker.is_dual_wielding then
		value = value + 19
	end
	local to_hit = GetRatingBonus("CR_HIT", attacker.hit_rating, attacker.level) 
	if attacker.talent_hit_bonus then
		to_hit = to_hit + attacker.talent_hit_bonus
	end
	value = value - to_hit
	if value < 1 then
		value = 1
	end
	return value
end

-- return the miss rate of the attacker on a given target
local function GetMissChance(attacker, target)
	local value = GetBaseAttackerMissChance(attacker)
	
	-- based on http://elitistjerks.com/360874-post32.html
	local mult = target.is_mob and 0.25 or 0.04
	
	value = value + GetRelativeDefenseValue(attacker, target) * mult
	
	if value < 1 then
		value = 1
	end
	
	return value
end

local function GetBaseTargetDodgeChance(target)
	if target.is_mob then
		-- based on http://elitistjerks.com/360874-post32.html
		return 3.3
	end
	local value = GetBaseDataPoint("dodge", target)
	
	value = value + GetRatingBonus("CR_DODGE", target.dodge_rating, target.level)
	
	if target.race == "NIGHTELF" then
		value = value + 1
	end
	if target.talent_dodge_bonus then
		value = value + target.talent_dodge_bonus
	end
	
	return value
end

local function GetDodgeChance(attacker, target)
	local value = GetBaseTargetDodgeChance(target)
	
	local mult
	if target.is_mob then
		-- based on http://elitistjerks.com/360874-post32.html
		mult = 0.1
	else
		mult = 0.04
	end
	
	value = value + GetRelativeDefenseValue(attacker, target) * mult
	value = value - GetExpertiseBonus(attacker) * 0.25
	
	
	if value < 0 then
		value = 0
	end
	
	return value
end

local function GetBaseTargetParryChance(target)
	if target.is_mob then
		-- based on http://elitistjerks.com/360874-post32.html
		return 11.5
	end
	if not target.can_parry then
		return 0
	end
	
	return 5 + target.talent_parry_bonus + GetRatingBonus("CR_PARRY", target.parry_rating, target.level)
end

local function GetParryChance(attacker, target)
	if attacker.is_behind then return 0 end
	local value = GetBaseTargetParryChance(target)
	
	local mult = target.is_mob and 0.01 or 0.04
	-- based on http://elitistjerks.com/360874-post32.html
	value = value + GetRelativeDefenseValue(attacker, target) * mult
	value = value - GetExpertiseBonus(attacker) * 0.25

	if value < 0 then
		value = 0
	end
	
	return value
end

local function GetBaseTargetBlockChance(target)
	if target.is_mob then
		-- based on http://elitistjerks.com/360874-post32.html
		return 4.4
	end
	if not target.can_block then
		return 0
	end
	
	local value = 5
	
	if target.talent_block_bonus then
		value = value + target.talent_block_bonus
	end
	
	value = value + GetRatingBonus("CR_BLOCK", target.block_rating, target.level)
	
	if target.buff_block_bonus then
		value = value + target.buff_block_bonus
	end
	
	return value
end

local function GetBlockChance(attacker, target)
	if attacker.is_behind then return 0 end
	local value = GetBaseTargetBlockChance(target)
	
	-- based on http://elitistjerks.com/360874-post32.html
	local mult = target.is_mob and 0.01 or 0.04
	value = value + GetRelativeDefenseValue(attacker, target) * mult
	if value < 0 then
		value = 0
	end
	
	return value
end

local function GetGlancingBlowChance(attacker, target)
	if attacker.is_mob then
		return 0
	end
	local v = GetRelativeDefenseValue(attacker, target, nil, true)
	if v > 0 then
		return 10 + v
	end
	return 0
end

local function GetCrushingBlowChance(attacker, target)
	if not attacker.is_mob or target.is_mob then return 0 end
	
	local v = - GetRelativeDefenseValue(attacker, target, true)
	return math.max(2 * v - 15, 0)
end

local function GetBaseAttackerCriticalHitChance(attacker)
	if attacker.is_mob then
		return 5
	end
	
	local value = GetRatingBonus("CR_CRIT", attacker.crit_rating, attacker.level)
	
	if attacker.talent_crit_bonus then
		value = value + attacker.talent_crit_bonus
	end
	
	value = value + GetBaseDataPoint("crit", attacker)
	
	return value -- fixme
end

local function GetCriticalHitChance(attacker, target)
	local value = GetBaseAttackerCriticalHitChance(attacker)
	
	value = value - GetRelativeDefenseValue(attacker, target) * 0.04
	if value < 0 then
		value = 0
	end
	return value
end

local function GetRemainingChance(values, total)
	for _, value in pairs(values) do
		total = total - value
	end
	if total < 0 then
		return 0
	end
	return total
end

local function GetCombatTable(attacker, target)
	local values = {}
	
	values.MISS = GetMissChance(attacker, target)
	values.DODGE = GetDodgeChance(attacker, target)
	values.PARRY = GetParryChance(attacker, target)
	values.BLOCK = GetBlockChance(attacker, target)
	values.GLANCING_BLOW = GetGlancingBlowChance(attacker, target)
	values.CRUSHING_BLOW = GetCrushingBlowChance(attacker, target)
	values.CRITICAL_HIT = GetCriticalHitChance(attacker, target)
	values.HIT = GetRemainingChance(values, 100)
	
	local sum = 0
	for _, entry in ipairs(combat_table_order) do
		local base = values[entry] or 0
		
		local dest = sum + base
		local value
		if dest >= 100 then
			value = base - (dest - 100)
		else
			value = base
		end
		values[entry] = value
		sum = sum + value
--~ 		print(entry, base, value)
	end

	return values
end

local function GetWeaponSpeed(unit, weapon)
	local base_speed = weapon.speed
	
	local haste = GetRatingBonus("CR_HASTE", unit.haste_rating, unit.level)
	
	return base_speed / (1 + haste)
end

local function GetWeaponDPS(unit, weapon)
	return (weapon.min + weapon.max) / (2 * GetWeaponSpeed(unit, weapon))
end

local function GetUnitDPSInfo(unit)
	local ap_dps = unit.attack_power / 14
	local dps = GetWeaponDPS(unit, unit.left_hand_weapon) + ap_dps
	if unit.is_dual_wielding then
		dps = dps + (GetWeaponDPS(unit, unit.right_hand_weapon) + ap_dps) / 2
	end
	return dps, unit.left_hand_weapon.speed
end

local function GetArmorPenetrationBonus(unit)
	local value = unit.armor_penetration_bonus or 0
	value = value + (unit.talent_armor_penetration_bonus or 0)
	value = value + (unit.buff_armor_penetration_bonus or 0)
	
	return value
end

local function GetArmorMitigation(attacker, target)
	local armor = target.armor - GetArmorPenetrationBonus(attacker)
	local level = attacker.level
	if level > 59 then
		level = level + 4.5 * (level - 59)
	end
	local temp = armor / (85 * level + 400)
	local mitigation = temp / (1 + temp)
	if mitigation > 0.75 then
		return 0.75
	end
	if mitigation < 0 then
		return 0
	end
	return mitigation
end

local function GetCritMultiplier(unit)
	local value = 200
	value = value + (unit.crit_mult_bonus or 0)
	value = value + (unit.talent_crit_mult_bonus or 0)
	
	return value / 100
end

local function GetDamageBonus(target)
	if target.is_mob or target.class ~= "WARRIOR" then
		return 1
	end
	if target.stance == "Defensive" then
		return 0.9
	elseif target.stance == "Berzerk" then
		return 1.2
	else
		return 1
	end
end

local function GetBlockValue(unit)
	return unit.block_value or 0
end

local function GetAverageGlancingMultiplier(attacker, target)
	local diff = GetRelativeDefenseValue(attacker, target, nil, true)
--~ 	local max = math.max(0.1, math.min(0.91, 1.3 - diff * 0.05))
--~ 	local min = math.max(0.2, math.min(0.99, 1.2 - diff * 0.03))
--~ 	local value = (max + min) / 2
	
	-- based on http://elitistjerks.com/360874-post32.html
	local value = 1 - ((1 - .75) * diff / 15)
	return value
end

local function GetExpectedDPS(attacker, target)
	local ct = GetCombatTable(attacker, target)
	
	local base, speed = GetUnitDPSInfo(attacker)
	
	local mitigated = base * (1 - GetArmorMitigation(attacker, target)) * GetDamageBonus(target)
	
	local result = 0
	for _, entry in ipairs(combat_table_order) do
		local mult = combat_table_dps_multiplier[entry]
		local percent = ct[entry] / 100
		local contribution
		if percent > 0 then
			if mult == "BLOCK" then
				local block = GetBlockValue(target) * speed
				local b = math.max(0, base - block)
				mult = b / base
			elseif mult == "GLANCING" then
				mult = GetAverageGlancingMultiplier(attacker, target)
			elseif mult == "CRIT" then
				mult = GetCritMultiplier(attacker)
			end
			contribution = mitigated * mult * percent
		else
			contribution = 0
		end
		result = result + contribution
--~ 		print(entry, base, mitigated, contribution, result)
	end
--~ 	print("DPS", result)
	return result
end

local Apophis = {
	name = "Apophis",
	is_mob = false,
	is_dual_wielding = false,
	can_parry = true,
	can_block = true,
	
	level = 70,
	class = "WARRIOR",
	race = "HUMAN",

	agility = 184,
	
	defense_skill = 350,
	
	weapon_skill = {
		sword = 355,
	},
	
--~ 	buff_block_bonus = 75,
	
	talent_defense_bonus = 20,
	talent_hit_bonus = 0,
	talent_dodge_bonus = 0,
	talent_parry_bonus = 5,
	talent_block_bonus = 5,

	hit_rating = 0,
	dodge_rating = 217,
	block_rating = 120,
	weapon_rating = 0,
	defense_rating = 382,
	parry_rating = 45,
}

local rogue = {
	is_mob = false,
	is_dual_wielding = true,
	can_parry = true,
	can_block = false,
	
	level = 70,
	class = "ROGUE",
	race = "HUMAN",
	
	agility = 387,
	
	weapon_skill = 355,

	talent_weapon_bonus = 10,
	talent_hit_bonus = 5,
	talent_crit_bonus = 5,
	talent_dodge_bonus = 3,
	talent_parry_bonus = 5,
	
	hit_rating = 175,
	crit_rating = 316,
}

local Jarbir = {
	name = "Jarbir",
	is_mob = false,
	is_dual_wielding = true,
	can_parry = true,
	can_block = false,
	
	level = 70,
	class = "ROGUE",
	race = "HUMAN",
	
	agility = 397,
	
	weapon_skill = {
		dagger = 350,
		sword = 355,
		fist = 341,
		mace = 270,
	},

	talent_weapon_bonus = {
		dagger = 10,
		sword = 10,
		fist = 10,
	},
	
	weapon_rating = {},
	
	talent_hit_bonus = 5,
	talent_crit_bonus = 10,
	talent_dodge_bonus = 3,
	talent_parry_bonus = 0,
	
	hit_rating = 270,
	crit_rating = 243,
	attack_power = 1554,
	
	-- crit_mult_bonus = 3,
	
	right_hand_weapon = {
		type = "dagger",
		min = 140,
		max = 211,
		speed = 1.8,
	},
	left_hand_weapon = {
		type = "dagger",
		min = 95,
		max = 178,
		speed = 1.4,
	},
}

local Khaldyr = {
	name = "Khaldyr",
	is_mob = false,
	is_dual_wielding = true,
--	is_behind = false,
	can_parry = true,
	can_block = false,
	
	level = 70,
	class = "ROGUE",
	race = "GNOME",
	
	agility = 497,
	
	weapon_skill = {
		dagger = 350,
		sword = 322,
		fist = 350,
		mace = 350,
	},

	talent_weapon_bonus = {
		dagger = 10,
		sword = 10,
		fist = 10,
	},
	talent_hit_bonus = 5,
	talent_crit_bonus = 10,
	talent_dodge_bonus = 2,
	talent_parry_bonus = 0,
	
	weapon_rating = {},
	hit_rating = 205,
	crit_rating = 182,
	attack_power = 1757,
	
	crit_mult_bonus = 3, -- meta-gem
	
	left_hand_weapon = {
		type = "fist",
		min = 166,
		max = 309,
		speed = 2.6
	},
	right_hand_weapon = {
		type = "fist",
		min = 123,
		max = 229,
		speed = 1.8,
	},
}

local Jharheck = {
	name = "Jharheck",
	is_mob = false,
	is_dual_wielding = true,
	can_parry = true,
	can_block = false,
	
	level = 65,
	class = "ROGUE",
	race = "DWARF",
	
	agility = 408,
	
	weapon_skill = {
		dagger = 321,
		sword = 319
	},

	left_hand_weapon = {
		type = "sword",
		min = 69,
		max = 128,
		speed = 1.9,
	},
	right_hand_weapon = {
		type = "dagger",
		min = 71,
		max = 133,
		speed = 1.8,
	},

	talent_weapon_bonus = {},
	talent_hit_bonus = 0,
	talent_crit_bonus = 5,
	talent_dodge_bonus = 0,
	talent_parry_bonus = 0,
	
	weapon_rating = {},
	hit_rating = 65,
	crit_rating = 58,
	attack_power = 1053,
}

local mage = {
	name = "mage",
	is_mob = false,
	is_dual_wielding = false,
	can_parry = false,
	can_block = false,
	
	level = 70,
	class = "MAGE",
	race = "GNOME",
	
	agility = 50,
	defense_skill = 350,
}

local boss = {
	name = "boss",
	is_mob = true,
	level = 73,
	armor = 6200, -- http://elitistjerks.com/f31/t16629-raid_boss_armor_values/
}

local function generic_mob(level)
	if not level then level = 70 end
	return {
		name = ("level %d mob"):format(level),
		is_mob = true,
		level = level,
		armor = level*50,
	}
end

function PrintCombatTable(attacker, target)
	print(("Combat Table of %s against %s:"):format(attacker.name, target.name))
	local ct = GetCombatTable(attacker, target)
	for _, entry in ipairs(combat_table_order) do
		print(("%s\t%.1f%%"):format(entry, ct[entry]))
	end
end

function PrintExpectedDPS(attacker, target)
	local value = GetExpectedDPS(attacker, target)
	print(("Expected white DPS of %s against %s : %3.2f"):format(attacker.name, target.name, value))
end

--~ print(string.format("%.2f%%", (20 + GetRatingBonus("CR_DEFENSE", 353, 70)) * 0.04))
--~ print(string.format("%.2f%%", GetRatingBonus("CR_BLOCK", 110, 70)))

--~ PrintCombatTable(boss, Apophis)
--~ PrintExpectedDPS(Jarbir, boss)
--~ PrintExpectedDPS(Khaldyr, boss)
--~ PrintExpectedDPS(Jharheck, boss)
--~ PrintExpectedDPS(Jharheck, generic_mob(Jharheck.level))

--[[ -- based on http://elitistjerks.com/360874-post32.html
local paladin1 = {
	name = "Paladin1",
	level = 70,
	class = "PALADIN",
	race = "HUMAN",
	agility = 80,
	
	right_hand_weapon = { type = "sword" },
	weapon_skill = { sword = 350 },
	talent_weapon_bonus = {},
	weapon_rating = {},
}

local paladin2 = {
	name = "Paladin2",
	level = 70,
	class = "PALADIN",
	race = "HUMAN",
	agility = 83,
	
	right_hand_weapon = { type = "sword" },
	weapon_skill = { sword = 365 },
	talent_weapon_bonus = {},
	weapon_rating = {},
}

local Venoxis = {
	name = "Venoxis",
	is_mob = true,
	level = 73,
}

PrintCombatTable(paladin1, Venoxis)
PrintCombatTable(paladin2, Venoxis)
]]

print(19 * 1 / GetRatingBonus("CR_HIT", 1, 70))
